package com.hm.base.auto.helper;

import java.lang.reflect.Method;
import java.util.Arrays;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.task.TaskExecutor;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.hm.base.auto.SystemEnvConfigProperties;
import com.hm.base.service.AuthorizationService;
import com.hm.base.service.ReqLogService;
import com.hm.base.vo.ReqLogVo;
import com.hm.common.annotation.WebClientOperationLog;
import com.hm.common.util.CommonUtil;
import com.hm.common.util.EncryptUtil.AES;
import com.hm.common.util.RemoteClientUtil;
import com.hm.core.ApiAspectSuport;
import com.hm.core.SdkConfig;

import lombok.extern.slf4j.Slf4j;

/**
 * @author shishun.wang
 * @date 上午10:21:53 2017年6月5日
 * @version 1.0
 * @describe
 */
@Slf4j
@Aspect
@Component
public class ApiAspectHandler {

	@Autowired
	private SystemEnvConfigProperties systemEnvConfigProperties;

	@Autowired
	private AuthorizationService authorizationService;

	@Autowired
	private ApiAspectSuport apiAspectSuport;

	@Autowired
	private ReqLogService reqLogService;

	@Autowired
	private TaskExecutor taskExecutor;

	@Around("execution(* com.hm.base.api.**Api.*(..))")
	public Object process(ProceedingJoinPoint point) throws Throwable {
		Method method = ((MethodSignature) point.getSignature()).getMethod();
		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
		apiAspectSuport.process(method, request, authorizationService, systemEnvConfigProperties.isDevModel(), systemEnvConfigProperties.isOpenDataAuth());
		return point.proceed();
	}

	@AfterReturning("@annotation(operationLog)")
	public void oplogProcess(JoinPoint point, WebClientOperationLog operationLog) throws Throwable {
		final String reqParams = Arrays.toString(point.getArgs());
		final HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
		taskExecutor.execute(new Runnable() {

			@Override
			public void run() {
				String token = request.getHeader(SdkConfig.REMOTE_CLIENT_AUTHORIZATION);

				if (CommonUtil.isEmpty(token)) {// 兼容session登陆用户
					HttpSession session = request.getSession();
					if (null == session) {
						return;
					}
					Object obj = session.getAttribute(SdkConfig.REMOTE_CLIENT_AUTHORIZATION);
					if (null == obj) {
						return;
					}
					token = obj.toString();
				}

				try {
					Long subscriberId = -1l;
					if (CommonUtil.isNotEmpty(token)) {
						String[] decrypt = AES.decrypt(token).split(":");
						subscriberId = Long.valueOf(decrypt[0]);
					}

					String describe = executeTemplate(operationLog.describe(), point);
					ReqLogVo vo = new ReqLogVo();
					{
						vo.setReqIp(RemoteClientUtil.getPackHost(request));
						vo.setPlatform(operationLog.platform());
						vo.setModule(operationLog.module());
						vo.setMethod(request.getMethod());
						String uri = request.getRequestURI() + "";
						vo.setUri(uri);
						vo.setParams(reqParams);
						vo.setDescribes(describe);
						vo.setCreateUser(subscriberId);
					}
					reqLogService.addReqLog(vo);
				} catch (Exception e) {
					log.error("存储请求操作日志记录失败", e);
				}
			}
		});
	}

	private String executeTemplate(String template, JoinPoint joinPoint) {
		LocalVariableTableParameterNameDiscoverer parameterNameDiscovere = new LocalVariableTableParameterNameDiscoverer();
		Method method = getMethod(joinPoint);
		String[] parameterNames = parameterNameDiscovere.getParameterNames(method);

		EvaluationContext context = new StandardEvaluationContext();
		ExpressionParser parser = new SpelExpressionParser();
		Object[] args = joinPoint.getArgs();
		if (args.length == parameterNames.length) {
			for (int i = 0, len = args.length; i < len; i++) {
				context.setVariable(parameterNames[i], args[i]);
			}
		}
		return parser.parseExpression(template, new TemplateParserContext()).getValue(context, String.class);
	}

	private Method getMethod(JoinPoint joinPoint) {
		Object target = joinPoint.getTarget();
		Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
		try {
			method = target.getClass().getMethod(method.getName(), method.getParameterTypes());
		} catch (Exception e) {
			log.error("解析请求日志参数失败", e);
		}
		return method;
	}
}